#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# new Env("联通抢券");
# cron: 55 9,17 * * *

import os
import re
import json
import requests
import datetime
import time
import pytz
import random
import threading
from notify import send
# ---------- 可调参数 ----------
MASTER_SWITCH = True          # 脚本总开关
PRINT_LOG     = True          # 是否打印日志
TARGET_COUPON = "30-3"        # 默认抢30-3券，可选：20-1 50-5 100-10 200-18
ACCOUNT_INDEX = 1             # 使用第几个账号（从1开始）
WAIT_SEC      = -0.5          # 负数表示提前发送请求（单位：秒），建议设置为-0.3到-0.5之间
TARGET_TIMES = ["10:00", "18:00"]       # 目标抢兑时间（北京时区），格式为"HH:MM"
CALIBRATE_OFFSET = 0          # 时间校准偏移量（毫秒），可根据实际情况调整
RETRY_COUNT = 3               # 抢券失败后的重试次数
RETRY_INTERVAL = 0.1          # 重试间隔（秒）
ENABLE_CONCURRENT_CHECK = True # 是否启用并发检查库存

# ---------- 常量 ----------
URL_LIST = "https://act.10010.com/SigninApp/new_convert/prizeList"
URL_CONV = "https://act.10010.com/SigninApp/convert/prizeConvert"
URL_CONF = "https://act.10010.com/SigninApp/convert/prizeConvertConfirm"
URL_RES  = "https://act.10010.com/SigninApp/convert/prizeConvertResult"

UA = ("Mozilla/5.0 (Linux; Android 12; GM1900 Build/SKQ1.211113.001; wv) "
      "AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/97.0.4692.98 Mobile Safari/537.36; "
      "unicom{version:android@12.0700,desmobile:0};devicetype{deviceBrand:OnePlus,deviceModel:GM1900};OSVersion/12;ltst")

COUPON_MAP = {
    "20-1":   {"amt": 1,   "min": 20},
    "30-3":   {"amt": 3,   "min": 30},
    "50-5":   {"amt": 5,   "min": 50},
    "100-10": {"amt": 10,  "min": 100},
    "200-18": {"amt": 18,  "min": 200},
}

# ---------- 全局变量 ----------
target_info = None  # 存储目标券信息，用于快速抢券
stop_check = False  # 控制库存检查线程

# ---------- 工具 ----------
def log(*args):
    if PRINT_LOG:
        print(f"[{now()}]", *args)

def now():
    # 获取北京时间
    beijing_tz = pytz.timezone('Asia/Shanghai')
    return datetime.datetime.now(beijing_tz).strftime("%m-%d %H:%M:%S.%f")[:-3]

def get_beijing_time():
    """获取当前北京时间，精确到毫秒"""
    beijing_tz = pytz.timezone('Asia/Shanghai')
    now = datetime.datetime.now(beijing_tz)
    # 添加校准偏移量
    if CALIBRATE_OFFSET != 0:
        now = now + datetime.timedelta(milliseconds=CALIBRATE_OFFSET)
    return now

def wait_until_exact_time(target_datetime):
    """精确等待到指定时间，毫秒级精度"""
    while True:
        current_time = get_beijing_time()
        time_diff = (target_datetime - current_time).total_seconds()
        
        # 当距离目标时间小于0.5秒时，使用高精度等待
        if time_diff > 0.5:
            # 先等待一段时间，剩下0.5秒时进行高精度等待
            time.sleep(time_diff - 0.5)
        elif time_diff > 0:
            # 使用更小的时间片进行高精度等待
            time.sleep(min(time_diff, 0.001))  # 最小1毫秒的睡眠
        else:
            # 时间已到，退出等待
            break

def parse_time_string(time_str):
    """解析时间字符串，返回小时和分钟"""
    try:
        # 支持"HH:MM"格式
        hour, minute = map(int, time_str.split(':'))
        if not (0 <= hour <= 23 and 0 <= minute <= 59):
            raise ValueError("时间格式错误：小时应在0-23之间，分钟应在0-59之间")
        return hour, minute
    except Exception as e:
        log(f"时间格式错误: {time_str}, 正确格式应为'HH:MM'")
        raise ValueError(f"时间格式错误: {time_str}") from e

def time_to_seconds(hour, minute, second=0):
    """将时分秒转换为一天中的秒数"""
    return hour * 3600 + minute * 60 + second

def wait_until_target_time(target_hour, target_minute):
    """阻塞到指定的时:分，毫秒级精度，支持提前发送"""
    global stop_check
    stop_check = False
    
    while True:
        current_time = get_beijing_time()
        
        # 计算当前时间的总秒数和目标时间的总秒数
        current_seconds = time_to_seconds(current_time.hour, current_time.minute, current_time.second)
        target_seconds = time_to_seconds(target_hour, target_minute)
        
        # 计算目标时间
        if current_seconds > target_seconds:
            # 等待到下一天的目标时间
            target_datetime = current_time.replace(
                hour=target_hour, minute=target_minute, second=0, microsecond=0
            ) + datetime.timedelta(days=1)
        elif current_seconds < target_seconds:
            # 等待到当天的目标时间
            target_datetime = current_time.replace(
                hour=target_hour, minute=target_minute, second=0, microsecond=0
            )
        else:
            # 当前就是目标时间附近
            if current_time.second > 10:
                # 超过10秒，等待下一个目标时间
                target_datetime = current_time.replace(
                    hour=target_hour, minute=target_minute, second=0, microsecond=0
                ) + datetime.timedelta(days=1)
            else:
                # 接近目标时间，设置目标时间
                target_datetime = current_time.replace(
                    minute=target_minute, second=0, microsecond=0
                )
        
        # 计算等待时间
        wait_seconds = (target_datetime - current_time).total_seconds()
        
        # 检查等待时间是否超过30分钟，如果是则结束任务
        if wait_seconds > 30 * 60:
            log(f"等待时间超过30分钟（{wait_seconds:.1f}秒），结束任务")
            return False
        
        if wait_seconds > 60:
            # 距离目标时间超过1分钟，显示等待信息并等待
            log(f"等待 {int(wait_seconds)} 秒后到达 {target_hour:02d}:{target_minute:02d}:00")
            # 为了避免长时间阻塞，最多等待1分钟
            time.sleep(min(wait_seconds, 60))
        elif wait_seconds > 10:
            # 距离目标时间10-60秒，开始预热并每隔几秒检查一次
            log(f"即将到达抢券时间，开始预热...")
            # 预热阶段，可以在这里做一些准备工作
            time.sleep(min(wait_seconds, 5))
        elif wait_seconds > 0:
            # 距离目标时间很近，开始精确等待
            log(f"精确等待中，{wait_seconds:.3f}秒后抢券")
            
            # 添加提前量，计算最终发送时间
            final_send_time = target_datetime + datetime.timedelta(seconds=WAIT_SEC)
            
            # 使用毫秒级精度等待
            wait_until_exact_time(final_send_time)
            return True
        else:
            # 已经超过目标时间，直接退出
            return False

def check_stock_concurrently(unicom_instance):
    """并发检查库存，提前获取目标券信息"""
    global target_info, stop_check
    last_check_time = 0
    check_interval = 2  # 初始检查间隔为2秒
    
    while not stop_check:
        current_time = time.time()
        # 控制检查频率，避免过于频繁的请求
        if current_time - last_check_time > check_interval:
            last_check_time = current_time
            
            try:
                # 获取券列表
                data = unicom_instance.get_prize_list()
                if data:
                    # 查找目标券
                    temp_target = find_target_quick(data)
                    if temp_target:
                        target_info = temp_target
                        log(f"发现目标券: {temp_target['name']}, 库存: {temp_target['stock']}")
                        # 当接近抢券时间时，提高检查频率
                        check_interval = 0.5
                    else:
                        # 没找到目标券，保持较低频率
                        check_interval = 2
            except Exception as e:
                if PRINT_LOG and random.random() < 0.1:  # 随机打印错误，避免日志过多
                    log(f"库存检查异常: {e}")
        
        # 短暂睡眠，避免CPU占用过高
        time.sleep(0.1)

def find_target_quick(data: dict):
    """快速查找目标券"""
    try:
        for tab in data.get("data", {}).get("datails", {}).get("tabItems", []):
            if tab.get("state") != "抢兑中":
                continue
            for item in tab.get("timeLimitQuanListData", []):
                amt = int(item["eb_count"])
                need = COUPON_MAP[TARGET_COUPON]["amt"]
                if need == amt and int(item["stockSurplus"]) > 0 and item["able_exchange"] == "0":
                    return {
                        "pid": item["product_id"],
                        "type_code": item["type_code"],
                        "name": item["product_name"],
                        "stock": int(item["stockSurplus"]),
                        "amt": amt
                    }
    except Exception as e:
        if PRINT_LOG:
            log(f"查找目标券异常: {e}")
    return None

# ---------- 环境变量 ----------
def get_env():
    ck = os.getenv("UNICOM_COOKIE", "").strip()
    if not ck:
        send("联通抢券", "❌ 未配置 UNICOM_COOKIE")
        exit(0)
    cookies = re.split(r'\n|&&', ck)
    if ACCOUNT_INDEX > len(cookies) or ACCOUNT_INDEX < 1:
        send("联通抢券", f"❌ 账号索引 {ACCOUNT_INDEX} 超出范围")
        exit(0)
    return cookies[ACCOUNT_INDEX - 1].strip()

# ---------- 业务 ----------
class Unicom:
    def __init__(self, cookie: str):
        self.s = requests.Session()
        # 配置session以提高性能
        self.s.keep_alive = True
        self.s.adapters.DEFAULT_RETRIES = 0
        
        # 设置超时时间，避免请求阻塞太久
        self.timeout = 5
        
        # 设置headers
        self.s.headers.update({
            "Cookie": cookie,
            "User-Agent": UA,
            "Referer": "https://img.client.10010.com/",
            "Origin": "https://img.client.10010.com",
            "X-Requested-With": "com.sinovatech.unicom.ui",
            "Accept": "application/json, text/plain, */*",
            "Content-Type": "application/x-www-form-urlencoded",
            # 添加一些可能有用的头信息
            "Accept-Encoding": "gzip, deflate",
            "Connection": "keep-alive"
        })

    def get_prize_list(self):
        try:
            # 使用较短的超时时间
            resp = self.s.post(URL_LIST, data="", timeout=self.timeout)
            resp.encoding = 'utf-8'
            return resp.json()
        except Exception as e:
            if PRINT_LOG:
                log(f"获取券列表失败: {e}")
            return None

    def find_target(self, data: dict):
        return find_target_quick(data)

    def do_convert(self, pid: str, type_code: str):
        try:
            # 1. 申请兑换 - 使用较短的超时时间
            start_time = time.time()
            r1 = self.s.post(URL_CONV, data={"product_id": pid, "typeCode": type_code}, timeout=3)
            if r1.json().get("status") != "0000":
                log(f"申请兑换失败: {r1.text}")
                return False
            uuid1 = r1.json()["data"]["uuid"]
            log(f"申请兑换耗时: {(time.time() - start_time)*1000:.2f}ms")

            # 2. 二次确认 - 快速处理
            start_time = time.time()
            r2 = self.s.post(URL_CONF, data={"uuid": uuid1}, timeout=3)
            if r2.json().get("errorCode") != "00000":
                log(f"二次确认失败: {r2.text}")
                return False
            uuid2 = r2.json()["data"]["uuid"]
            log(f"二次确认耗时: {(time.time() - start_time)*1000:.2f}ms")

            # 3. 最终兑换 - 关键步骤
            start_time = time.time()
            r3 = self.s.post(URL_RES, data={"uuid": uuid2}, timeout=3)
            ok = r3.json().get("errorCode") == "00000"
            log(f"最终兑换耗时: {(time.time() - start_time)*1000:.2f}ms")
            
            if ok:
                log(f"兑换成功: {r3.json()['data']['userproVo']['product_name']}")
            else:
                log(f"兑换失败: {r3.text}")
            return ok
        except Exception as e:
            log(f"兑换过程异常: {e}")
            return False

    def run(self):
        log(f"开始处理账号 {ACCOUNT_INDEX}")
        
        # 优先使用预先获取的目标信息
        global target_info
        if target_info:
            log(f"使用预获取的目标券: {target_info['name']}, 库存: {target_info['stock']}")
            target = target_info
        else:
            # 预获取失败，重新获取
            data = self.get_prize_list()
            if not data:
                return "获取券列表失败"
            target = self.find_target(data)
            if not target:
                return f"目标券 {TARGET_COUPON} 暂无库存"
            log(f"找到目标券: {target['name']}, 库存: {target['stock']}")
        
        # 尝试抢券，支持重试
        for attempt in range(1, RETRY_COUNT + 1):
            log(f"第 {attempt} 次抢券尝试")
            suc = self.do_convert(target["pid"], target["type_code"])
            if suc:
                return "兑换成功"
            if attempt < RETRY_COUNT:
                log(f"抢券失败，{RETRY_INTERVAL}秒后重试...")
                time.sleep(RETRY_INTERVAL)
        
        return "兑换失败（已重试最大次数）"

# ---------- main ----------
def main():
    if not MASTER_SWITCH:
        log("【总开关】已关闭，脚本结束")
        return
    
    try:
        # 验证并解析目标时间列表
        parsed_times = []
        for time_str in TARGET_TIMES:
            hour, minute = parse_time_string(time_str)
            parsed_times.append((hour, minute, time_str))
        
        cookie = get_env()
        u = Unicom(cookie)
        
        # 获取当前北京时间
        current_time = get_beijing_time()
        current_seconds = time_to_seconds(current_time.hour, current_time.minute, current_time.second)
        
        # 找到下一个目标时间
        next_target = None
        min_diff = float('inf')
        
        for hour, minute, time_str in parsed_times:
            target_seconds = time_to_seconds(hour, minute)
            diff = target_seconds - current_seconds
            if diff > 0 and diff < min_diff:
                min_diff = diff
                next_target = (hour, minute, time_str)
        
        # 如果当天没有找到下一个目标时间，则选择第一个目标时间（第二天）
        if next_target is None:
            hour, minute, time_str = parsed_times[0]
            next_target = (hour, minute, time_str)
        
        target_hour, target_minute, target_time_str = next_target
        log(f"预热完成，等待 {target_time_str} 抢券")
        
        # 预热：提前登录、TCP握手、Cookie刷新
        log("开始预热...")
        _ = u.get_prize_list()
        
        # 启动并发库存检查线程（如果启用）
        check_thread = None
        global stop_check, target_info
        stop_check = False
        target_info = None
        
        if ENABLE_CONCURRENT_CHECK:
            log("启动并发库存检查...")
            check_thread = threading.Thread(target=check_stock_concurrently, args=(u,), daemon=True)
            check_thread.start()
        
        try:
            # 等待到目标时间，支持提前发送
            # 检查等待是否成功（如果等待时间超过30分钟或已过期，会返回False）
            if not wait_until_target_time(target_hour, target_minute):
                log("等待失败，结束任务")
                return
            
            # 停止库存检查
            stop_check = True
            if check_thread:
                check_thread.join(timeout=1)
            
            # 整点发出抢兑请求
            log("开始抢券...")
            result = u.run()
            send("联通抢券结果", result)
            log(f"抢券结果: {result}")
            
        except KeyboardInterrupt:
            log("用户中断操作")
        finally:
            # 确保停止库存检查
            stop_check = True
    except Exception as e:
        log(f"程序异常: {e}")
        send("联通抢券异常", f"程序运行异常: {str(e)}")

if __name__ == "__main__":
    # 测试环境变量
    os.environ["UNICOM_COOKIE"] = "MUT_S=android12; devicedId=c499c70ed4074040a40c46c12209b869; login_type=06; u_account=18734196943; d_deviceCode=77a7b949a264438498ab68b0fd7e628f; c_mobile=18734196943; wo_family=0; u_areaCode=; city=019|190|91369894|-99; random_login=0; mallcity=19|190; cdn_area=19|190; SHOP_PROV_CITY=; a_token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NDQ4ODk4ODIsInRva2VuIjp7ImxvZ2luVXNlciI6IjE4NzM0MTk2OTQzIiwicmFuZG9tU3RyIjoieWhhZTU0MjMxNzQ0Mjg1MDgyIn0sImlhdCI6MTc0NDI4NTA4Mn0.B-vTPRIXkctoX9cRChUqyspZ2y6zJ5JNovIivddT0TJKrTGTMbkNZwiq2mRRIojexlKhRdElsD3x54-BOSHIdA; jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtb2JpbGUiOiIxODczNDE5Njk0MyIsInBybyI6IjAxOSIsImNpdHkiOiIxOTAiLCJpZCI6IjM1ZWNjYmU3N2I2M2Q0NmE2YWU3NzFlZGE3NWE1ODljIn0.V88KRc5aQoLRSggusqYanZ95TzYTC-GNcGJnain1XqA; sdkuuid=3cd35a2e976b498fb111b83223d3def3; c_version=android@12.0700; cw_mutual=78b45157a4b2dddda11b0c0c6ebca754ff669bbd6f4103eb13f13889f0a24621f33ee9d493725c68038fbdf3294445af5e7c5b7a79a296f4f2af128eb805c323; _uop_id=npfauth_jv184s4600412a296b5ac29fb32b5e31fbbf9af1v68mnvgo; MUT_V=android%254012.0700; TOKEN_UID=Fa8UU2LkggB7Q3fh9YXjdg==; TOKEN_UID_USER_TYPE=g2y2w+bimyc+WAcE7L7kEA==; TOKEN_USER_TYPE=g2y2w+bimyc+WAcE7L7kEA==; TOKEN_USER_NET=1; TOKEN_UID_NAME=O7zzmZmWkIfRViV8Qsq9odxPL9ETlFnfWS5gXRUP7DakxX5xg14RbuR2Z+VFqjY0kquUSXjfBD823vyyl0f3v7KyD16dlJid/795ZHUQMSZjcpZSagptZ1fhwq0IdiSys9PCsu0U7nvz3b3AuZXX78y1Q5vFKlVD8RLF+RVXqB/6vFUcHyq3nt0IHrcAmfqb/pd2D52BhPUqgCtGAtgW4yf436ko+KFSV+osTzN7jWc=; MUT=7ffdc03d-a16d-428b-a0bd-d03a71a5e7f3; usercity=19|190; TOKEN_NET=UNI; tianjin_ip=0; SUCCESSBANNER=; SUCCESSPOPUP=; TOKEN_UID_ALL=%7B%22userNick%22%3A%22JUU3JThFJThCJUU1JUI4JTg1kbe%22%2C%22phoneNum%22%3A%2218734196943%22%2C%22certNum%22%3A%22142703********0339%22%7D; custId=18734196943; certNum=142703********0339; tianjincity=11|110; tfstk=gCJK2ptkE2lnvV4UvUciEgjUze--nfxebe-bEab3NFL9SNjCPT1BfCLW76bC-2W8wELNELxC-uLJcNyHxHXo28KyqwvnYwbWW3-YKUXQOla63eEhrJoSeT6eIB2l-2WJVEX-oqDmnH-FLTgmoKacgEW5VYbSs6Oadw9qoqDmIH-FUTgDrS8IflIP5M_5N_iTCi__FJT5R1a14gW5PUTSXh_wID5yPTi9fNS5FaT5FcK6NzNdrk_geBNdekYzWNw7FdIdOXtC58swBMCdlHNuF8FVv6QXANu0Vf8CMe1JUme9WBTepOfx5DB66eJRiL4IfT157_9BfmFfyBxDpsIPLKvAz1acbOFKXcFza6sZDzzFKG1UImsOocqga75KbGQmXcFza6sNXZmwi7PP9c5..; PvSessionId=20250930090448f61d1958ff50402e884f806edb355088; c_id=a3345a2895e0163327b10c73b6f8f1db45f4777cdf4d8ac0c4a7ad9ff8a9b562; enc_acc=MlgUTlYCMM2DusKGVVtd1FPldPHj7sMREtkhP3oYe746I08EqKMNLjuF+hZrdEXEMOo97nQGUR/24Ays4KIGlhPrkXCSS+T8p1aU0Ziaj9xG+TNV5l2FpE/gIHijF4y0IIzXtGXXeuZKZFmnzJtEaLuLIqj/yfjwSnMX7dNx/Ds=; ecs_acc=MlgUTlYCMM2DusKGVVtd1FPldPHj7sMREtkhP3oYe746I08EqKMNLjuF+hZrdEXEMOo97nQGUR/24Ays4KIGlhPrkXCSS+T8p1aU0Ziaj9xG+TNV5l2FpE/gIHijF4y0IIzXtGXXeuZKZFmnzJtEaLuLIqj/yfjwSnMX7dNx/Ds=; t3_token=cd3b7cf83b2452e3079d373a08b8c3c5; invalid_at=b5d3d09ee72fdd7a640f30e4857dca123e1888e1b137fb6df5cd46925008784f; third_token=eyJ2ZXJzaW9uIjoiMDAiLCJkYXRhIjoiODk4NTQzOGQ4M2EwODdlMDI2ODRjYjczM2U2OWYyOWY3MmUzN2MwZjZiN2NiOTFiZjdmYWZmZTFjYTkzNTQ3MWQxOGI5YTI1YWVjN2I0MjVjNzZiNWI1ZWE3Mzc1YzA2ZGY5OWYwMjkyNGVjYjQxZTIzNWQwNmZlYzMwYTJlNmZlZTM2NTFiZTNmNmY4NmNiMTYwNWU5MTJkM2MxMDUwYiJ9; ecs_token=eyJ2ZXJzaW9uIjoiMDAiLCJkYXRhIjoiNWVjMzc1MzNjZDhiYmJhZTEwYWQ1NDMzYjIyNDJkODc5YWRlZjgxOTUwNzU4ZmQzZmI5N2NlNWY3Yzg3MTEzN2JhNzU4YTU0MDEzY2M2ZGRiOWM5Y2VkMzlmMmJhODNkY2NhMjkxYzQ1NjQ0ZjA2MWZhYjdlMGI2MjJjMzY4MDc3Mjk5Njk3NWRhYjcyNzU2NmE0YmRmODk1NTFiNDZkNmFjN2RiNjBmOTk5ZjVlMDIxYjI1YjkyM2VjNWRjNjUzNGQyMGU3YWY0OWRkNDU2NjI3YjIyYmE1ZmE0MWRjMmRhMDQyMDhlYjk4MWFmMTYxM2U5ZTEyZmFjMDU0OTczMjVhMzNkZTkyOWU3OTk2NGY3Zjc3ODA4MzQwYzBkOTY4ZTdmMWZiMDIxM2UzNWQ4MDRhZDA2ZWJjOGE5ZTBlNjRmOGZlNjMyZTZhNjdhNDY2ZjAyNmVkMzFkNDMxYjA5YWViMzdiM2JkYzhjNTA2MjRmZmI1NWUyM2Y4YTY1NGE4YjlmYTNlNWJlNzZlNWM4ODZiZmZiOWM4NmE0NWQ5N2EifQ==; SHAREJSESSIONID=497B917224F863292C136B03B933AB79; unicomMallUid=18734196943; ecs_cook=fcbad1489f5567914a53f8352a762239&&MUT_S=android14; devicedId=7dd77c050ccf467ba11db74a7c9df0a7; c_version=android@12.0700; PvSessionId=202510051527119f30bf482a884f88827dd04306f6255e; c_id=d0db107b0c25c49eba855e07e9701100386621000ccde4e9f2a70fc0332383de; login_type=06; u_account=15582623376; city=019|194|90340197|-99; d_deviceCode=b31592441af046469e74aa9e1c25603a; enc_acc=Az1t9vFbaBQSkqlS75ZIh4fqbi8Qw2WufEsnOw5YYIEYG35PwhghvIxIKLX3riPjwq0FKeBg1OU5AB4wZzkDJTFeYuSF9NR2b6WYbyp2QI6Q8yPllBh1z6wZlKbGf387dqsJdI8y36xMO36aOu3BTtJNMBGJG6aGPaZbXyMUp+o=; ecs_acc=Az1t9vFbaBQSkqlS75ZIh4fqbi8Qw2WufEsnOw5YYIEYG35PwhghvIxIKLX3riPjwq0FKeBg1OU5AB4wZzkDJTFeYuSF9NR2b6WYbyp2QI6Q8yPllBh1z6wZlKbGf387dqsJdI8y36xMO36aOu3BTtJNMBGJG6aGPaZbXyMUp+o=; random_login=1; cw_mutual=6ff66a046d4cb9a67af6f2af5f74c3211476ac72273de20f0e142359a384e3934802166f023c308882ab097f816096407b2f27b9acc91f156a7cfb0734164ae5; t3_token=01b0fdc5b33a2a1a703c8b83df190897; invalid_at=13c2266cb86994ea3422bd1199621a699515c8ad04731dbd92a0dd1798bbaaf0; c_mobile=15582623376; wo_family=0; u_areaCode=; third_token=eyJkYXRhIjoiNzg0ODBiNzQ0ODQ5OGZiNWY4MWU4NWRiZjUwNjJlMzIyY2E3YTdlZDI0YjI5NGRiNGY5Zjc2N2Q0MWY5ODVlNWNlYTUzMTc4MWVkOTE4YzcxNTZmZjdmMDFjZWM5YTc5MDBhYjIwZThhYTU4N2MxMDhkYjA1Zjk4Y2M5Zjg1OTBlMGM1YzU3NmQwNjFkMGJiYmZlZjU0Y2UwMjgyZjMxNiIsInZlcnNpb24iOiIwMCJ9; ecs_token=eyJkYXRhIjoiZjFlZDA5YzU1NjBkOTcyOTU4MjRmYTY5ZmQ2MTgwZTI3ZjcxNGQxNTA1MzkzZGJmYmM3YTk0NzViZTcyMTlhOWVkNWExMzBhOTk1NWMyNWZjZTdmNWQyNGVjYjczODg4YmRmNDAzYmFjODUwZjA4MzhkOGE4ZWI2MWY4M2M5ZDA0NDgwMGY2ZjAzMDNiMzIxNDMzYzZlNTc2MTZiNzAzZmI0MjYwNmM0Y2M2Mzc2ZmRhZjNmNTU1OWMwNGNlYjI3NmFkOGI4N2IxY2JmOTk5MjljMzA2YmY5MGZjMjdlYWU4Mjg4ZTY4NDAxMTU2YWUyYjE3YjNjZWEzM2RlNTJjMTg0NjFkYjdjMGRmNDY5NGM1ZTY3Yzg3MDI1NDUxNGVhYmFiMDZlZTBiMWY2OWRmNTdkYWE4OTU2YjlmMDgzZjYyZjIwN2Q2YTNiMjExOGE2ZDc0YWZjZGNlZWQyNzFjMzgyYWYwZTRmZDcyM2U1MjQyZDcwMmEwMWM0OWJiYTUzYzRjZmFiYzEwMGUyYTk3MWZhNTliZmYzNWFjNWJjYWEiLCJ2ZXJzaW9uIjoiMDAifQ==; SHAREJSESSIONID=4272FAD3A4E445D451B8B04E1401F202; unicomMallUid=15582623376; ecs_cook=af3fadd4a360aa0f3ae1aa1f8ca98d53"
    main()
